/**
 *  Copyright (c) 1997-2013, www.tinygroup.org (luo_guo@icloud.com).
 *
 *  Licensed under the GPL, Version 3.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *       http://www.gnu.org/licenses/gpl.html
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package org.tinygroup.dbf.impl;


import org.tinygroup.dbf.*;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;


/**
 * Created by luoguo on 2014/7/10.
 */
public class StreamDbfWriter implements DbfWriter {
    private DbfDocument dbfDocument;
    private String encode;

    private void checkDbfDocument() throws IOException {
        DbfHeader header = dbfDocument.getHeader();
        List<DbfField> fields = dbfDocument.getFields();
        DbfDataSet dataSet = dbfDocument.getDataSet();
        int recordCountInHeader = header.getRecordCount(), recordCountInDataSet = dataSet.getDbfRecordCount();
        if (recordCountInHeader != recordCountInDataSet) {
            throw new IOException(String.format("文档头里记录数为%d,而实际记录的数为%d", recordCountInHeader, recordCountInDataSet));
        }

        int recordLengthInHeader = header.getRecordLength(), recordLengthInFields = 1;
        for (DbfField field : fields) {
            recordLengthInFields += field.getLength();
        }

        if (recordLengthInHeader != recordLengthInFields) {
            throw new IOException(String.format("文档头里字段长度为%d,而实际的字段长度为%d", recordLengthInHeader, recordLengthInFields));
        }

        int headerLengthInHeader = header.getHeaderLength(), headerLengthReal = ((fields.size() + 1) << 5) + 1;
        if (headerLengthInHeader != headerLengthReal) {
            throw new IOException(String.format("文档头里headerLength为%d,而实际的headerLength为%d", headerLengthInHeader, headerLengthReal));
        }
    }

    private void writeDbfHeader(OutputStream outputStream) throws IOException {
        DbfHeader header = dbfDocument.getHeader();
        outputStream.write(dbfDocument.getType());
        outputStream.write(DbfUtil.dateToByteArray(header.getLastUpdate()));
        outputStream.write(DbfUtil.intToByteArray(header.getRecordCount(), RECORD_COUNT_LENGTH));
        outputStream.write(DbfUtil.intToByteArray(header.getHeaderLength(), HEADER_LENGTH_LENGTH));
        outputStream.write(DbfUtil.intToByteArray(header.getRecordLength(), RECORD_LENGTH_LENGTH));
        outputStream.write(new byte[20]);
    }

    private void writeField(OutputStream outputStream, DbfField field) throws IOException {
        byte[] fieldNameFull = field.getName().getBytes(encode);
        byte[] fieldNameReal = new byte[NAME_LENGTH];
        int fieldNameReallength = fieldNameFull.length > NAME_LENGTH ? NAME_LENGTH : fieldNameFull.length;
        System.arraycopy(fieldNameFull, 0, fieldNameReal, 0, fieldNameReallength);
        outputStream.write(fieldNameReal);
        outputStream.write(0);
        outputStream.write((byte) field.getType());
        outputStream.write(DbfUtil.intToByteArray(field.getDisplacement(), 4));
        outputStream.write(DbfUtil.intToByteArray(field.getLength(), 1));
        outputStream.write(DbfUtil.intToByteArray(field.getDecimal(), 1));
        outputStream.write(new byte[14]);
    }

    private void writeDbfFields(OutputStream outputStream) throws IOException {
        List<DbfField> fields = dbfDocument.getFields();
        for (DbfField field : fields) {
            writeField(outputStream, field);
        }
        outputStream.write(HEADER_END_CHAR);

    }


    private void writeRecord(OutputStream outputStream, DbfField field, Object value) throws IOException {

        byte[] binaryData = DbfUtil.getBinary(value, field, encode);
        int needFillLength = field.getLength() - binaryData.length;
        byte[] reservedBits = new byte[needFillLength];
        for (int j = 0; j < needFillLength; j++) {
            reservedBits[j] = 0x20;
        }
        outputStream.write(reservedBits);
        outputStream.write(binaryData);
    }

    private void writeDbfRecord(OutputStream outputStream) throws IOException {
        DbfDataSet dataSet = dbfDocument.getDataSet();
        while (dataSet.next()) {

            DbfRecord record = dataSet.getDbfRecord();
            List<Object> dataList = record.getDataList();
            int dataIndex = 0;
            if (record.isDeleted()) {
                outputStream.write((byte) '*');
            } else {
                outputStream.write(0x20);
            }
            for (Object data : dataList) {
                DbfField field = dbfDocument.getFields().get(dataIndex);
                writeRecord(outputStream, field, data);
                dataIndex++;
            }
        }
    }

    public void writeDbf(DbfDocument dbfDocument, File file, String encode) throws IOException {
        this.dbfDocument = dbfDocument;
        this.encode = encode;
        FileOutputStream outputStream = new FileOutputStream(file);
        checkDbfDocument();
        writeDbfHeader(outputStream);
        writeDbfFields(outputStream);
        writeDbfRecord(outputStream);
        outputStream.close();

    }

    public void writeDbf(DbfDocument dbfDocument, String fileName, String encode) throws IOException {
        writeDbf(dbfDocument, new File(fileName), encode);
    }
}
